﻿using ServiceModelEx;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;

namespace RockBus.ServiceModel
{
    public static class ServiceHostExtensions
    {
        public const string OpeningDescription = "ServiceHost Opened on urls:";
        public const string ClosingDescription = "ServiceHost Closed on urls:";

        public static void TraceHostDetails(this ServiceHostBase serviceHost)
        {
            TraceHostDetails(serviceHost, OpeningDescription);
        }

        public static void TraceHostDetails(this ServiceHostBase serviceHost, string description)
        {
            Trace.WriteLine(GetHostDetails(serviceHost, description));
        }

        public static string GetHostDetails(this ServiceHostBase serviceHost)
        {
            return GetHostDetails(serviceHost, OpeningDescription);
        }

        public static string GetHostDetails(this ServiceHostBase serviceHost, string description)
        {
            if (null == serviceHost)
            {
                throw new ArgumentNullException("ServiceHost");
            }

            var sb = new StringBuilder();
            sb.AppendLine(description);
            foreach (ServiceEndpoint se in serviceHost.Description.Endpoints)
            {
                sb.AppendLine("  " + se.Contract.Name + ": " + se.Address.Uri.ToString());
            }
            return sb.ToString();
        }

        public static void EnsureQueuesAvailable(this ServiceHostBase serviceHost)
        {
            if (null == serviceHost)
            {
                throw new ArgumentNullException("ServiceHost");
            }

            foreach (ServiceEndpoint se in serviceHost.Description.Endpoints)
            {
                se.EnsureQueueAvailable();
            }
        }

        public static Task<bool> TryOpenAsync(this ServiceHostBase serviceHost, int waitTimeoutMs)
        {
            if (null == serviceHost)
            {
                throw new ArgumentNullException("ServiceHost");
            }

            if (serviceHost.State != CommunicationState.Created)
            {
                throw new InvalidOperationException("Attempt to open a ServiceHost that is not in the Created state.");
            }

            return Task.Factory.FromAsync(
                serviceHost.BeginOpen(
                    TimeSpan.FromMilliseconds(waitTimeoutMs),
                    (asyncResult) => { ((ServiceHostBase)asyncResult.AsyncState).EndOpen(asyncResult); },
                    serviceHost),
                (asyncResult) => { return true; });
        }

        public static Task<bool> TryCloseAsync(this ServiceHostBase serviceHost, int waitTimeoutMs)
        {
            if (null == serviceHost)
            {
                throw new ArgumentNullException("ServiceHost");
            }

            if (serviceHost.State != CommunicationState.Opened)
            {
                throw new InvalidOperationException("Attempt to close a ServiceHost that is not in the Opened state.");
            }

            return Task.Factory.FromAsync(
                serviceHost.BeginClose(
                    TimeSpan.FromMilliseconds(waitTimeoutMs),
                    (asyncResult) => { ((ServiceHostBase)asyncResult.AsyncState).EndClose(asyncResult); },
                    serviceHost),
                (asyncResult) => { return true; });
        }
    }
}